﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Vortex.Drawing {

	/// <summary>
	/// 128 bit color structure represented by 4 float components
	/// </summary>
	public struct ColorF {
		///<summary>Alpha component of color</summary>
		public float A;

		///<summary>Red component of color</summary>
		public float R;

		///<summary>Green component of color</summary>
		public float G;

		///<summary>Blue component of color</summary>
		public float B;

		/// <summary>
		/// Initializes a new instance of the <see cref="ColorF"/> struct.
		/// </summary>
		/// <param name="r">The red component.</param>
		/// <param name="g">The green component.</param>
		/// <param name="b">The blue component.</param>
		/// <param name="a">The alpha component.</param>
		public ColorF(float r, float g, float b, float a) {
			A = a;
			R = r;
			G = g;
			B = b;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ColorF"/> struct.
		/// </summary>
		/// <param name="color">The integer color.</param>
		public ColorF(ColorU color) {
			A = color.Af;
			R = color.Rf;
			G = color.Gf;
			B = color.Bf;
		}

		#region Static Fast Operations

		/// <summary>
		/// Clamps the specified color. Makes sure all of components are in range 0..1
		/// </summary>
		/// <param name="color">The source color.</param>
		/// <param name="result">The result color.</param>
		public static void Clamp(ref ColorF color, out ColorF result) {
			result.A = MathHelper.Clamp(color.A, 0, 1);
			result.R = MathHelper.Clamp(color.R, 0, 1);
			result.G = MathHelper.Clamp(color.G, 0, 1);
			result.B = MathHelper.Clamp(color.B, 0, 1);
		}

		/// <summary>
		/// Negates the specified color. Makes sure all of components are in range 0..1
		/// </summary>
		/// <param name="color">The source color.</param>
		/// <param name="result">The result color.</param>
		public static void Negate(ref ColorF color, out ColorF result) {
			result.A = -color.A;
			result.R = -color.R;
			result.G = -color.G;
			result.B = -color.B;
		}

		/// <summary>
		/// Adds specified colors per component.
		/// </summary>
		/// <param name="left">The left operands.</param>
		/// <param name="right">The right operand.</param>
		/// <param name="result">The result color.</param>
		public static void Add(ref ColorF left, ref ColorF right, out ColorF result) {
			result.A = left.A + right.A;
			result.R = left.R + right.R;
			result.G = left.G + right.G;
			result.B = left.B + right.B;
		}

		/// <summary>
		/// Subtracts specified colors per component.
		/// </summary>
		/// <param name="left">The left operands.</param>
		/// <param name="right">The right operand.</param>
		/// <param name="result">The result color.</param>
		public static void Subtract(ref ColorF left, ref ColorF right, out ColorF result) {
			result.A = left.A - right.A;
			result.R = left.R - right.R;
			result.G = left.G - right.G;
			result.B = left.B - right.B;
		}

		/// <summary>
		/// Multiplies specified colors per component.
		/// </summary>
		/// <param name="left">The left operands.</param>
		/// <param name="right">The right operand.</param>
		/// <param name="result">The result color.</param>
		public static void Multiply(ref ColorF left, ref ColorF right, out ColorF result) {
			result.A = left.A * right.A;
			result.R = left.R * right.R;
			result.G = left.G * right.G;
			result.B = left.B * right.B;
		}

		/// <summary>
		/// Adds specified color and value per component.
		/// </summary>
		/// <param name="color">The left operands.</param>
		/// <param name="value">The right operand.</param>
		/// <param name="result">The result color.</param>
		public static void Add(ref ColorF color, float value, out ColorF result) {
			result.A = color.A + value;
			result.R = color.R + value;
			result.G = color.G + value;
			result.B = color.B + value;
		}

		/// <summary>
		/// Subtracts specified color and value per component.
		/// </summary>
		/// <param name="color">The left operands.</param>
		/// <param name="value">The right operand.</param>
		/// <param name="result">The result color.</param>
		public static void Subtract(ref ColorF color, float value, out ColorF result) {
			result.A = color.A - value;
			result.R = color.R - value;
			result.G = color.G - value;
			result.B = color.B - value;
		}

		/// <summary>
		/// Multiplies specified color and value per component.
		/// </summary>
		/// <param name="color">The left operands.</param>
		/// <param name="value">The right operand.</param>
		/// <param name="result">The result color.</param>
		public static void Multiply(ref ColorF color, float value, out ColorF result) {
			result.A = color.A * value;
			result.R = color.R * value;
			result.G = color.G * value;
			result.B = color.B * value;
		}

		/// <summary>
		/// Calculates interpolated color between two specified
		/// </summary>
		/// <param name="from">The left operands.</param>
		/// <param name="to">The right operand.</param>
		/// <param name="factor">The factor of interpolation.</param>
		/// <param name="result">The interpolated color.</param>
		public static void Lerp(ref ColorF from, ref ColorF to, float factor, out ColorF result) {
			result.A = from.A * (to.A - from.A) * factor;
			result.R = from.R * (to.R - from.R) * factor;
			result.G = from.G * (to.G - from.G) * factor;
			result.B = from.B * (to.B - from.B) * factor;
		}

		/// <summary>
		/// Compares specified colors
		/// </summary>
		/// <param name="left">The left operand.</param>
		/// <param name="right">The right operand.</param>
		/// <returns><c>true</c> if both colors are equal</returns>
		public static bool Equals(ref ColorF left, ref ColorF right) {
			return
				left.A == right.A &&
				left.R == right.R &&
				left.G == right.G &&
				left.B == right.B;
		}


		#endregion

		#region Special Methods

		/// <summary>
		/// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
		/// </summary>
		/// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
		/// <returns>
		/// 	<c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
		/// </returns>
		public override bool Equals(object obj) {
			if (obj is ColorF) {
				ColorF color = (ColorF)obj;
				return Equals(ref this, ref color);
			} else {
				return false;
			}
		}

		/// <summary>
		/// Returns a hash code for this instance.
		/// </summary>
		/// <returns>
		/// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
		/// </returns>
		public override int GetHashCode() {
			return A.GetHashCode() ^ R.GetHashCode() ^ G.GetHashCode() ^ B.GetHashCode();
		}

		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this color.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this color.
		/// </returns>
		public override string ToString() {
			return string.Format(
				"{{{0:3F},{1:3F},{2:3F},{3:3F}}}",
				A, R, G, B
			);
		}


		#endregion

		#region Operators

		public static implicit operator ColorU(ColorF color) {
			return new ColorU(color.R, color.G, color.B, color.A);
		}

		public static implicit operator ColorF(ColorU color) {
			return new ColorF(color.Rf, color.Gf, color.Bf, color.Af);
		}

		#endregion
	}
}
